﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace AllDemo
{
    public partial class 练习两个小球碰撞 : Form
    {
        public 练习两个小球碰撞()
        {
            InitializeComponent();
           
        }
        private delegate void DelegateGDIMove(List<Circle> cs);
        private bool isRun = true;
        Thread t=null;
        private void 练习两个小球碰撞_Load(object sender, EventArgs e)
        {
            pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            t = new Thread(new ThreadStart(CrossThreedFlush));
            t.IsBackground = true;
            t.Start();
        }
        private void CrossThreedFlush()
        {
            List<Circle> csList = new List<Circle>();
            Circle cs1 = new Circle();
            cs1.Center = new PointF(100, 50);
            cs1.Radius = 20;
            cs1.MoveSpeed = 6.0f;
            cs1.Weight = cs1.Radius * cs1.Radius * cs1.Radius;
            cs1.Angle = Math.PI * 0.3;
            cs1.CircleColor = Color.Red;
            csList.Add(cs1);

            Circle cs2 = new Circle();
            cs2.Center = new PointF(50, 50);
            cs2.Radius = 25;
            cs2.MoveSpeed = 6.0f;
            cs2.Weight = cs2.Radius * cs2.Radius * cs2.Radius;
            cs2.Angle = Math.PI * 0.25;
            cs2.CircleColor = Color.Green;
            csList.Add(cs2);


            Circle cs3 = new Circle();
            cs3.Center = new PointF(150, 500);
            cs3.Radius = 30;
            cs3.MoveSpeed = 6.0f;
            cs3.Weight = cs3.Radius * cs3.Radius * cs3.Radius;
            cs3.Angle = Math.PI * 1.3;
            cs3.CircleColor = Color.Blue;
            csList.Add(cs3);

            Circle cs4 = new Circle();
            cs4.Center = new PointF(200, 500);
            cs4.Radius = 50;
            cs4.MoveSpeed = 6.0f;
            cs4.Weight = cs4.Radius * cs4.Radius * cs4.Radius;
            cs4.Angle = Math.PI * 1.9;
            cs4.CircleColor = Color.Orange;
            csList.Add(cs4);

            while (isRun)
            {
                CircleMove(csList);
                Thread.Sleep(10);
            }
        }

        

        public void CircleMove(List<Circle> csList)
        {
            //初始化面板
            Bitmap bmp = (Bitmap)pictureBox1.Image;
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.HighQuality;  //图片柔顺模式选择
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;//高质量
            g.CompositingQuality = CompositingQuality.HighQuality;//再加一点
            g.Clear(Color.White);
            g.DrawRectangle(Pens.Black, 0, 0, pictureBox1.Width - 1, pictureBox1.Height - 1);
            
            //画球
            for (int i = 0; i < csList.Count; i++)
            {
                g.FillEllipse(new SolidBrush(csList[i].CircleColor), csList[i].Center.X - csList[i].Radius, csList[i].Center.Y - csList[i].Radius, csList[i].Radius * 2, csList[i].Radius * 2);
                if (i > 0)
                {
                    g.DrawLine(Pens.Red, csList[i - 1].Center, csList[i].Center);
                }
                if (i == csList.Count - 1)
                {
                    g.DrawLine(Pens.Red, csList[0].Center, csList[csList.Count - 1].Center);
                }

            }
            g.Dispose();
            pictureBox1.Invoke(new Action(()=> 
            { 
                pictureBox1.Refresh();
            }));
            GoNextPoint(csList);
        }

        public void GoNextPoint(List<Circle> csList)
        {
            PointF[] nextPi = new PointF[csList.Count];
            for (int i = 0; i < csList.Count; i++)
            {
                nextPi[i] = new PointF(csList[i].Center.X + (float)Math.Cos(csList[i].Angle) * csList[i].MoveSpeed, csList[i].Center.Y + (float)Math.Sin(csList[i].Angle) * csList[i].MoveSpeed);
            }

            for (int i = 0; i < csList.Count; i++)
            {
                //碰撞到左右边界
                if (nextPi[i].X + csList[i].Radius > pictureBox1.Width || nextPi[i].X - csList[i].Radius < 0)
                {
                   
                    csList[i].Angle = (Math.PI*3 - csList[i].Angle) % (Math.PI*2);
                   
                }
                else if (nextPi[i].Y + csList[i].Radius > pictureBox1.Height || nextPi[i].Y - +csList[i].Radius < 0)
                {//碰撞到上下边界
                    csList[i].Angle = (2 * Math.PI - csList[i].Angle) % (2 * Math.PI);
                }

                nextPi[i] = new PointF(csList[i].Center.X + (float)Math.Cos(csList[i].Angle) * csList[i].MoveSpeed, csList[i].Center.Y + (float)Math.Sin(csList[i].Angle) * csList[i].MoveSpeed);
                csList[i].Center = nextPi[i];
            }
            for (int i = 0; i < csList.Count; i++)
            {
                for (int j = i + 1; j < csList.Count; j++)
                {
                    //第i个小球和其后面的cs.Length-i个小球发生碰撞可能
                    double center_distance = Math.Sqrt(Math.Pow(nextPi[i].X - nextPi[j].X, 2) + Math.Pow(nextPi[i].Y - nextPi[j].Y, 2));
                    //判断两个圆是否相交
                    if (center_distance <= csList[i].Radius + csList[j].Radius)   
                    {//碰撞发生
                        //ruuConsole.WriteLine("*******---"+cs[i].CircleColor.ToKnownColor()+"------->"+cs[j].CircleColor.ToKnownColor()+"---******\r\n"+cs[i].ToString() + "----------------------------\r\n" + cs[j].ToString() + "----------------------------\r\n");
                        CalculateDumped2(csList[i], csList[j]);
                    }
                }
            }

        }

        public void CalculateDumped2(Circle c1, Circle c2)
        {
            double m1 = c1.Weight;
            double m2 = c2.Weight;
            double v1 = c1.MoveSpeed;
            double v2 = c2.MoveSpeed;
            double d1 = c1.Angle;
            double d2 = c2.Angle;
            //求小球去向像一个位置的偏移量
            double v1x = v1 * Math.Cos(d1);
            double v1y = v1 * Math.Sin(d1);
            double v2x = v2 * Math.Cos(d2);
            double v2y = v2 * Math.Sin(d2);
            v1x = Math.Round(v1x, 6);
            v2x = Math.Round(v2x, 6);
            v1y = Math.Round(v1y, 6);
            v2y = Math.Round(v2y, 6);
            double v1x_, v1y_, v2x_, v2y_;

            CalculateDumped_Line(m1, m2, v1x, v2x, out v1x_, out v2x_);
            CalculateDumped_Line(m1, m2, v1y, v2y, out v1y_, out v2y_);

            if (Verify(m1, m2, v1x, v2x, v1x_, v2x_) != 0)
            {
                MessageBox.Show("水平方向不守恒");
            }
            if (Verify(m1, m2, v1y, v2y, v1y_, v2y_) != 0)
            {
                MessageBox.Show("垂直方向不守恒");
            }

            c1.MoveSpeed = (float)Math.Sqrt(v1x_ * v1x_ + v1y_ * v1y_);
            c2.MoveSpeed = (float)Math.Sqrt(v2x_ * v2x_ + v2y_ * v2y_);

            c1.Angle = GetDirect(v1x_, v1y_);
            c2.Angle = GetDirect(v2x_, v2y_);
        }
        public void CalculateDumped_Line(double m1, double m2, double v1, double v2, out double v1_, out double v2_)
        {
            double x = m1 * v1 + m2 * v2;
            double a = 0.5 * m1 + 0.5 * m1 * m1 / m2;
            double b = x * m1 / m2;
            v1_ = (Math.Sqrt(0.5 * m1 * v1 * v1 + 0.5 * m2 * v2 * v2 - x * x / 2 / m2 + b * b / 4 / a) + b / 2 / Math.Sqrt(a)) / Math.Sqrt(a);
            if (Math.Round(v1_, 6) == v1)
            {
                v1_ = (-Math.Sqrt(0.5 * m1 * v1 * v1 + 0.5 * m2 * v2 * v2 - x * x / 2 / m2 + b * b / 4 / a) + b / 2 / Math.Sqrt(a)) / Math.Sqrt(a);
            }
            v2_ = (m1 * v1 + m2 * v2 - m1 * v1_) / m2;

            if (double.IsNaN(v1_))
                v1_ = 0.0;
            if (double.IsNaN(v2_))
                v2_ = 0.0;
        }
        public int Verify(double m1, double m2, double v1, double v2, double v1_, double v2_)
        {
            //动能
            float KE_old = (float)(0.5 * m1 * v1 * v1 + 0.5 * m2 * v2 * v2);
            float KE_new = (float)(0.5 * m1 * v1_ * v1_ + 0.5 * m2 * v2_ * v2_);
            //动量
            float momentum_old = (float)(m1 * v1 + m2 * v2);
            float momentum_new = (float)(m1 * v1_ + m2 * v2_);

            int res = 0;
            if (KE_new != KE_old)
                res += 1;
            if (momentum_new != momentum_old)
                res += 2;
            return res;
        }


        public double GetDirect(double vx, double vy)
        {
            if (vx == 0)
            {
                return vy < 0 ? Math.PI * 1.5 : Math.PI * 0.5;
            }
            if (vy == 0)
            {
                return vx < 0 ? Math.PI : 0;
            }
            if (vx > 0 && vy > 0)
            {
                return Math.Abs(Math.Atan(vy / vx));
            }
            if (vx < 0 && vy > 0)
            {
                return Math.Abs(Math.Atan(vy / vx)) + Math.PI * 0.5;
            }
            if (vx < 0 && vy < 0)
            {
                return Math.Abs(Math.Atan(vy / vx)) + Math.PI * 1;
            }
            if (vx > 0 && vy < 0)
            {
                return Math.Abs(Math.Atan(vy / vx)) + Math.PI * 1.5;
            }
            return 100;
        }

        private void 练习两个小球碰撞_FormClosing(object sender, FormClosingEventArgs e)
        {
            isRun = false;
            
            Thread.Sleep(100);
            t.Abort();
        }
    }
}
